package cz.muni.fi.pa165.hauntedhouses.rest.server;

import com.fasterxml.jackson.databind.ObjectMapper;
import cz.muni.fi.pa165.hauntedhouses.dto.AbilityDTO;
import cz.muni.fi.pa165.hauntedhouses.dto.AbilityType;
import cz.muni.fi.pa165.hauntedhouses.dto.GhostDTO;
import cz.muni.fi.pa165.hauntedhouses.dto.HouseDTO;
import cz.muni.fi.pa165.hauntedhouses.dto.HumanDTO;
import cz.muni.fi.pa165.hauntedhouses.facade.ServiceFacade;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.ResourceBundle;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.context.support.SpringBeanAutowiringSupport;

/**
 * Servlet for HTTP POST and GET methods alowing play feature along with ghost entity manipulation.
 *
 * @author Siliarus
 */
@WebServlet(urlPatterns = "/rest/*")
public class PlayServlet extends HttpServlet {

    private ResourceBundle properties;
    
    @Autowired
    ServiceFacade facade;

    @Override
    public void init(ServletConfig config) throws ServletException {
        super.init(config);
        SpringBeanAutowiringSupport.processInjectionBasedOnServletContext(this,
                config.getServletContext());
        properties = ResourceBundle.getBundle("Resources");
    }

    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        String pathInfo = request.getPathInfo();

        if (pathInfo != null && pathInfo.matches("/")) {
            String nextJSP = "/rest_index.jsp";
            RequestDispatcher dispatcher = getServletContext().getRequestDispatcher(nextJSP);
            dispatcher.forward(request, response);
        }

        if (pathInfo != null && pathInfo.matches("/ghosts")) {
            response.setContentType("application/json");
            ObjectMapper mapper = new ObjectMapper();
            mapper.writeValue(response.getOutputStream(), facade.findAllGhosts());
        } else {
            if (pathInfo != null && pathInfo.matches("/houses")) {
                response.setContentType("application/json");
                ObjectMapper mapper = new ObjectMapper();
                List<HouseDTO> houses = new ArrayList<>();
                for (HouseDTO h : facade.findAllHouses()) {
                    for (HumanDTO human : h.getHumans()) {
                        human.setHome(null);
                    }
                    houses.add(h);
                }
                mapper.writeValue(response.getOutputStream(), houses);
            } else {
                if (pathInfo != null && pathInfo.matches("/abilities")) {
                    response.setContentType("application/json");
                    Long ghostId = Long.parseLong(request.getParameter("ghost"));
                    if (ghostId != null) {
                        GhostDTO dbGhost = facade.getGhost(ghostId);
                        if (dbGhost != null) {
                            ObjectMapper mapper = new ObjectMapper();
                            mapper.writeValue(response.getOutputStream(), dbGhost.getAbilities());
                        } else {
                            response.sendError(404, properties.getString("error.ghostparams"));
                        }

                    }
                } else {
                    if (pathInfo != null && pathInfo.matches("/humans")) {
                        response.setContentType("application/json");
                        Long houseId = Long.parseLong(request.getParameter("house"));
                        if (houseId != null) {
                            HouseDTO dbHouse = facade.getHouse(houseId);
                            if (dbHouse != null) {
                                List<HumanDTO> humans = new ArrayList<>();
                                for (HumanDTO human : dbHouse.getHumans()) {
                                    human.setHome(null);
                                    humans.add(human);
                                }
                                ObjectMapper mapper = new ObjectMapper();
                                mapper.writeValue(response.getOutputStream(), humans);
                            } else {
                                response.sendError(404, properties.getString("error.houseparams"));
                            }
                        }
                    } else {
                        if (pathInfo != null && pathInfo.matches("/ghost")) {
                            response.setContentType("application/json");
                            Long ghostId = Long.parseLong(request.getParameter("id"));
                            if (ghostId != null) {
                                GhostDTO dbGhost = facade.getGhost(ghostId);
                                if (dbGhost != null) {
                                    ObjectMapper mapper = new ObjectMapper();
                                    mapper.writeValue(response.getOutputStream(), dbGhost);
                                } else {
                                    response.sendError(404, properties.getString("error.nosuchghost"));
                                }
                            }
                        } else {
                            if (pathInfo != null && pathInfo.matches("/ability")) {
                                response.setContentType("application/json");
                                Long abilityId = Long.parseLong(request.getParameter("id"));
                                if (abilityId != null) {
                                    AbilityDTO dbAbility = facade.getAbility(abilityId);
                                    if (dbAbility != null) {
                                        ObjectMapper mapper = new ObjectMapper();
                                        mapper.writeValue(response.getOutputStream(), dbAbility);
                                    } else {
                                        response.sendError(404, properties.getString("error.nosuchability"));
                                    }
                                }
                            } else {
                                if (pathInfo != null && pathInfo.matches("/house")) {
                                    response.setContentType("application/json");
                                    Long houseId = Long.parseLong(request.getParameter("id"));
                                    if (houseId != null) {
                                        HouseDTO dbHouse = facade.getHouse(houseId);
                                        if (dbHouse != null) {
                                            for (HumanDTO human : dbHouse.getHumans()) {
                                                human.setHome(null);
                                            }
                                            ObjectMapper mapper = new ObjectMapper();
                                            mapper.writeValue(response.getOutputStream(), dbHouse);
                                        } else {
                                            response.sendError(404, properties.getString("error.nosuchhouse"));
                                        }
                                    }
                                } else {
                                    if (pathInfo != null && pathInfo.matches("/human")) {
                                        response.setContentType("application/json");
                                        Long humanId = Long.parseLong(request.getParameter("id"));
                                        if (humanId != null) {
                                            HumanDTO dbHuman = facade.getHuman(humanId);
                                            if (dbHuman != null) {
                                                dbHuman.setHome(null);
                                                ObjectMapper mapper = new ObjectMapper();
                                                mapper.writeValue(response.getOutputStream(), dbHuman);
                                            } else {
                                                response.sendError(404, properties.getString("error.nosuchhuman"));
                                            }
                                        }
                                    } else {
                                        if (pathInfo != null && pathInfo.matches("/abilitiesByType")) {
                                            response.setContentType("application/json");
                                            String abilityType = request.getParameter("type");
                                            if (abilityType != null) {
                                                ObjectMapper mapper = new ObjectMapper();
                                                mapper.writeValue(response.getOutputStream(), facade.findAbilitiesByType(AbilityType.valueOf(abilityType)));

                                            }
                                        }
                                    }

                                }
                            }
                        }
                    }
                }
            }
        }
    }

    @Override
    protected synchronized void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        request.setCharacterEncoding("utf-8");

        ObjectMapper mapper = new ObjectMapper();
        Map<String, Object> map = mapper.readValue(request.getInputStream(), HashMap.class);

        if (map.get("event").equals("update")) {
            GhostDTO ghost = mapper.convertValue(map.get("ghost"), GhostDTO.class);
            if (ghost != null && facade.getGhost(ghost.getId()) != null) {
                facade.updateGhost(ghost);
            } else {
                response.sendError(404, properties.getString("error.ghostnotfound"));
            }
        } else {
            if (map.get("event").equals("add")) {
                GhostDTO ghost = mapper.convertValue(map.get("ghost"), GhostDTO.class);
                if (ghost != null && ghost.getId() == null) {
                    facade.createGhost(ghost);
                } else {
                    response.sendError(404, properties.getString("error.invalidghost"));
                }
            } else {
                if (map.get("event").equals("haunt")) {
                    GhostDTO ghost = mapper.convertValue(map.get("ghost"), GhostDTO.class);
                    AbilityDTO ability = mapper.convertValue(map.get("ability"), AbilityDTO.class);
                    HumanDTO human = mapper.convertValue(map.get("human"), HumanDTO.class);
                    if (ghost != null && facade.getGhost(ghost.getId()) != null && ability != null && facade.getAbility(ability.getId()) != null && human != null && facade.getHuman(human.getId()) != null && ghost.getAbilities().contains(ability)) {
                        facade.useAbility(facade.getAbility(ability.getId()), facade.getHuman(human.getId()), facade.getGhost(ghost.getId()));
                    } else {
                        response.sendError(404, properties.getString("error.invalidparams"));
                    }
                } else {
                    if (map.get("event").equals("addAbility")) {
                        GhostDTO ghost = mapper.convertValue(map.get("ghost"), GhostDTO.class);
                        Long abilityId = mapper.convertValue(map.get("abilityId"), Long.class);
                        if (ghost != null && abilityId != null && facade.getGhost(ghost.getId()) != null) {
                            AbilityDTO ability = facade.getAbility(abilityId);
                            if (ability == null || ghost.getAbilities().contains(ability)) {
                                response.sendError(HttpServletResponse.SC_BAD_REQUEST, properties.getString("error.unknownrequest"));
                                return;
                            }
                            facade.grantNewAbility(ghost, ability);
                        }
                    } else {
                        response.sendError(HttpServletResponse.SC_BAD_REQUEST, properties.getString("error.unknownrequest"));
                    }
                }
            }

        }

    }

    @Override
    protected void doDelete(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        request.setCharacterEncoding("utf-8");

        String pathInfo = request.getPathInfo();
        if (pathInfo != null && pathInfo.contains("/deleteGhost")) {
            Long ghostId = Long.parseLong(pathInfo.substring(13));
            if (ghostId != null && facade.getGhost(ghostId) != null) {
                facade.removeGhost(facade.getGhost(ghostId));
            } else {
                response.sendError(404, properties.getString("error.ghostnotfound"));
            }
        }
    }
}
